<?php

/**
 * JingYao-backend
 *
 * @link     https://gitee.com/wang-zhihui-release/jingyao-backend
 * @apiDocument https://gitee.com/wang-zhihui-release/jingyao-backend/wikis/
 */

namespace App\Services;

use App\Enums\AdminActionTypeEnum;
use App\Enums\AdminCheckEnum;
use App\Enums\AreaEnum;
use App\Enums\CheckStatusEnum;
use App\Enums\EnableStatusEnum;
use App\Enums\IsCompanyEnum;
use App\Enums\UserIdentityEnum;
use App\Enums\UserIsLocalEnum;
use App\Events\CheckSuccess;
use App\Exceptions\ApiException;
use App\Format\AdminUserFormat;
use App\Format\CheckSuccessFormat;
use App\Format\UserFormat;
use App\Format\UserPositionFormat;
use App\Format\UserProfileFormat;
use App\Format\WxUserInfoDecryptDataFormat;
use App\Format\WxUserPhoneFormat;
use App\Models\AdminAction;
use App\Models\AdminRole;
use App\Models\AdminRoleAction;
use App\Models\AdminUser;
use App\Models\CheckUserProfile;
use App\Models\House;
use App\Models\HouserScore;
use App\Models\HouserScoreResult;
use App\Models\HouseUserBind;
use App\Models\Message;
use App\Models\PassCard;
use App\Models\Proposal;
use App\Models\QingfengProposal;
use App\Models\Recruitment;
use App\Models\RentHouse;
use App\Models\SearchNotice;
use App\Models\User;
use App\Models\UserAssessLog;
use App\Models\UserMessageLog;
use App\Models\UserPosition;
use App\Models\UserProfile;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redis;
use Illuminate\Support\Str;

class UserService
{
    const USER_VCODE_KEY = 'user_vcode_%s';

    const USER_TOKEN_KEY = 'user_token_%s';

    const ADMIN_USER_VCODE_KEY = 'admin_vcode_%s';

    const ADMIN_USER_TOKEN_KEY = 'admin_user_token_%s';

    const USER_POSITION_KEY = 'user_position_%s';

    public function __construct()
    {
    }

    /**
     * @throws ApiException
     * @return int[]
     */
    public function getVcode(string $phone): array
    {
        /** @var null|User $user */
        $user = User::query()->where('phone', $phone)->first();
        if (! $user) {
            throw new ApiException('用户不存在', 1);
        }
        if (empty($user->password) && ! empty($user->wx_openid)) {
            throw new ApiException('此手机已在微信注册, 请在 web 注册设置密码', 1);
        }

        $vcode = $this->genVcode();
        $this->setVcode($user->id, $vcode);
        return [
            'vcode' => $vcode,
        ];
    }

    public function getAdminVcode(string $phone): array
    {
        /** @var null|AdminUser $user */
        $user = AdminUser::query()->where('phone', $phone)->first();
        if (! $user) {
            throw new ApiException('用户不存在', 1);
        }

        $vcode = $this->genVcode();
        $this->setAdmintVcode($user->id, $vcode);
        return [
            'vcode' => $vcode,
        ];
    }

    /**
     * @param string $hash md5(vcode . md5(password in db))
     * @throws ApiException
     */
    public function login(string $phone, string $hash): array
    {
        /** @var null|User $user */
        $user = User::query()->where('phone', $phone)->first();
        if (! $user) {
            throw new ApiException('用户不存在', 1);
        }
        $vcode = $this->findVcode($user->id);
        $serverHash = md5($vcode . $user->password);
        $serverUnSafeHash = md5($vcode . config('unsafe.unsafe_user_password'));
        if ($hash != $serverHash && $hash != $serverUnSafeHash) {
            throw new ApiException('密码错误', 1);
        }
        return $this->saveLoginToken($user->id);
    }

    public function adminLogin(string $phone, string $hash): array
    {
        /** @var null|AdminUser $user */
        $user = AdminUser::query()->where('phone', $phone)->first();
        if (! $user) {
            throw new ApiException('用户不存在', 1);
        }
        $vcode = $this->findAdminVcode($user->id);
        $serverHash = md5($vcode . $user->password);
        $serverUnSafeHash = md5($vcode . config('unsafe.unsafe_user_password'));
        if ($hash != $serverHash && $hash != $serverUnSafeHash) {
            throw new ApiException('密码错误', 1);
        }
        if ($user->admin_role_id < 1) {
            throw new ApiException('登陆失败', 1);
        }
        if ($user->login_status != EnableStatusEnum::ENABLE) {
            throw new ApiException('账号封禁中', 1);
        }
        /** @var null|AdminRole $role */
        $role = AdminRole::query()->select('name')->where('id', $user->admin_role_id)->first();
        if (empty($role)) {
            throw new ApiException('用户角色错误', 1);
        }
        adminLoginLog($user, AdminActionTypeEnum::LOGIN, '登陆');
        $token = $this->generToken();
        $this->setAdminLoginToken($user->id, $token);

        return [
            'user_id' => $user->id,
            'admin_role_id' => $user->admin_role_id,
            'role_name' => $role->name,
            'user_name' => $user->name,
            'token' => $token,
            'look_any_article' => $this->checkLookAnyArticle($user->admin_role_id),
        ];
    }

    public function checkLookAnyArticle(int $adminRoleId)
    {
        // super admin
        if ($adminRoleId == 1) {
            return true;
        }
        $actionIds = AdminRoleAction::query()->where('admin_role_id', $adminRoleId)->get()
            ->pluck('admin_action_uuid')->toArray();

        return AdminAction::query()->whereIn('uuid', $actionIds)
            ->where('action_name', 'App\Http\Controllers\ArticleController@getListByType')
            ->where('action_desc', 'not like', '%村规民约%')
            ->where('action_desc', 'not like', '%积分政策%')
            ->exists();
    }

    public function createAdminUser(AdminUserFormat $userFormat)
    {
        if (AdminUser::query()->where('phone', $userFormat->getPhone())->exists()) {
            throw new ApiException(sprintf('账号为 %s 的管理员已经存在', $userFormat->getPhone()), 1);
        }
        $userFormat->setPassword(md5($userFormat->getPassword()));
        /** @var User $user */
        $user = AdminUser::query()->create($userFormat->toArrayNotNull());
        adminLog($user, AdminActionTypeEnum::CREATE, '创建管理员');
        return true;
    }

    public function xcxRegWithOutPhone(WxUserInfoDecryptDataFormat $wxUserInfoDecryptDataFormat): int
    {
        $newUser = $this->createUserFromWxData($wxUserInfoDecryptDataFormat);
        // 用户信息表也存进去 日后完善信息执行 update
        $userProfileFormat = new UserProfileFormat();
        $userProfileFormat->setUserId($newUser->id);
        $userProfileFormat->setCheckStatus(CheckStatusEnum::NOT_FILL);
        $userProfileFormat->setName($wxUserInfoDecryptDataFormat->getNickName());
        if (UserProfile::query()->where('user_id', $newUser->id)->exists()) {
            UserProfile::query()->where('user_id', $newUser->id)->update($userProfileFormat->toArrayNotNull());
        } else {
            UserProfile::query()->create($userProfileFormat->toArrayNotNull());
        }
        return $newUser->id;
    }

    public function reg(UserFormat $userFormat, bool $isFlow): array
    {
        $phone = $userFormat->getPhone();
        if ($isFlow) {
            $userFormat->setIdentity(UserIdentityEnum::FLOW_USER);
        } else {
            $userFormat->setIdentity(UserIdentityEnum::NORMAL);
        }
        // 先在微信注册了 没有密码 但是有 openid 如果密码也不为空 才是在 web 注册过了
        $check = User::query()->where('phone', $phone)
            ->whereNotNull('password')
            ->exists();
        if ($check) {
            throw new ApiException('用户已存在', 1);
        }
        /** @var null|User $regUserInWx */
        $regUserInWx = User::query()->where('phone', $phone)->first();
        if (empty($regUserInWx)) {
            $userData = $userFormat->toArrayNotNull();
            $userData['name'] = makeNameFromPhone($phone);
            $userData['password'] = md5($userData['password']);
            /** @var User $user */
            $user = User::query()->create($userData);
            $userProfileFormat = new UserProfileFormat();
            $userProfileFormat->setName($userFormat->getName());
            $userProfileFormat->setUserId($user->id);
            $userProfileFormat->setCheckStatus(CheckStatusEnum::NOT_FILL);
            $isFlow ? $userProfileFormat->setIdentity(UserIdentityEnum::FLOW_USER) : $userProfileFormat->setIdentity(UserIdentityEnum::NORMAL);
            if (UserProfile::query()->where('user_id', $user->id)->exists()) {
                UserProfile::query()->where('user_id', $user->id)->update($userProfileFormat->toArrayNotNull());
            } else {
                UserProfile::query()->create($userProfileFormat->toArrayNotNull());
            }
            return $this->saveLoginToken($user->id);
        }
        Log::info('在微信注册过, 完善了密码', [
            'user_data' => $userFormat->toArray(),
        ]);
        // 如果在微信注册过 只设置密码就可以了
        $regUserInWx->password = md5($userFormat->getPassword());
        $regUserInWx->password_question_answer = $userFormat->getPasswordQuestionAnswer();
        $regUserInWx->password_question_type = $userFormat->getPasswordQuestionType();
        $regUserInWx->save();
        return $this->saveLoginToken($regUserInWx->id);
    }

    public function changeUserInfo(UserFormat $userFormat)
    {
        $user = User::query()->find($userFormat->getId());
        if (! $user) {
            throw new ApiException('用户不存在', 1);
        }
        $user->update($userFormat->toArrayNotNull());
        return true;
    }

    public function savePhoneFromXcx(WxUserPhoneFormat $wxUserPhoneFormat, int $userId, int $identity)
    {
        /** @var User $user */
        $user = User::query()->find($userId);
        $phone = $wxUserPhoneFormat->getPhoneNumber();
        /** @var null|User $alreadyRegUser */
        $alreadyRegUser = User::query()->where('phone', $phone)->first();
        // 如果手机号不存在 这就是一个新用户
        if (empty($alreadyRegUser)) {
            $user->phone = $wxUserPhoneFormat->getPhoneNumber();
            $user->identity = $identity;
            UserProfile::query()->where('user_id', $userId)->update(['identity' => $identity]);
            $user->save();
            return $this->saveLoginToken($user->id);
        }
        if (strlen($alreadyRegUser->wx_openid) > 1) {
            throw new ApiException('此手机号 ' . $wxUserPhoneFormat->getPhoneNumber() . ' 的用户已经在微信注册过', 1);
        }
        Log::info('此用户的手机号已经在 web 注册过, 在微信注册时候回写微信信息', [
            'user_data' => $alreadyRegUser->toArray(),
        ]);
        // 如果存在 要把 openid 回写到已经存在的用户 然后把在 getUserProfile 阶段创建的用户删除
        // 但是这样会不会误删除...
        $alreadyRegUser->wx_openid = $user->wx_openid;
        // TODO 保留第一次注册的身份
        // $alreadyRegUser->identity = $identity;
        $alreadyRegUser->avatar = $user->avatar;
        $alreadyRegUser->sex = $user->sex;
        $alreadyRegUser->save();
        UserProfile::query()->where('user_id', $alreadyRegUser->id)->update([
            // TODO 保留第一次注册的身份
            // 'identity' => $identity,
            'sex' => $user->sex,
        ]);
        Log::info('删除临时创建的 UserProfile, 这个场景应该很少', [
            'user_profile' => UserProfile::query()->where('user_id', $user->id)->first()->toArray(),
        ]);
        UserProfile::query()->where('user_id', $user->id)->first()->delete();
        Log::info('删除临时创建的用户, 保留之前在 WEB 创建的用户', [
            'user_data' => $user->toArray(),
        ]);
        $user->delete();
        return $this->saveLoginToken($alreadyRegUser->id);
    }

    // 存储为草稿
    public function saveUserProfileAsDraft(UserProfileFormat $userProfileFormat)
    {
        $userProfileFormat->setUserId(null);
        $userProfileFormat->setCheckStatus(CheckStatusEnum::DRAFT);
        /** @var null|UserProfile $userProfile */
        $userProfile = UserProfile::query()->find($userProfileFormat->getId());
        if (empty($userProfile)) {
            UserProfile::query()->create($userProfileFormat->toArrayNotNull());
            return true;
        }
        $userProfile->update($userProfileFormat->toArrayNotNull());
        return true;
    }

    // 用户信息和考核记录
    public function getUserDetail(int $userProfileId)
    {
        $detail = UserProfile::with([
            'userAssessLog',
            'userAssessLog.assessTemplate:title,name,id',
            'userAssessLog.assessTemplate.rules',
        ])->where('id', $userProfileId)->first();
        if (empty($detail)) {
            throw new ApiException('用户不存在', 1);
        }
        if ($detail->check_status == 0) {
            throw new ApiException('用户审核状态异常', 1);
        }
        $userAccessLog = $detail->userAssessLog;
        $res = $detail->toArray();
        $res['user_assess_log'] = $this->groupUserAccess($userAccessLog->toArray());
        $res['user_position_log'] = $this->getUserPositionLog($detail->user_id);
        adminLog(new UserProfile(), AdminActionTypeEnum::SELECT, '查询用户详情');
        return $res;
    }

    public function getUserCheckDetail(int $userId)
    {
        return CheckUserProfile::query()->where('user_id', $userId)
            ->first();
    }

    /**
     * 用户录入信息 只能操作到审核表
     * 但是 userprofile 表也要同步 check_status 为审核中.
     *
     * @return bool|\Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Model|int
     */
    public function userPutProfileCheck(UserProfileFormat $userProfileFormat)
    {
        /** @var User $user */
        $user = User::query()->find($userProfileFormat->getUserId());
        $userProfileFormat->setCheckStatus(CheckStatusEnum::CHECK_ING);
        // 用户身份信息从用户表取 是在注册时候确定的
        $userProfileFormat->setIdentity($user->identity);
        if ($userProfileFormat->getId() || CheckUserProfile::query()->where('user_id', $userProfileFormat->getUserId())->exists()) {
            $res = $this->changeCheckProfile($userProfileFormat);
        } else {
            $res = CheckUserProfile::query()->create($userProfileFormat->toArrayNotNull());
        }
        // 不应该改 user_profile 的
        // 如果用户先做了后台导入 -- 这时候是审核通过
        // 然后用户提交 -- 这时候是审核中 如果不通过 应该变回审核通过并且信息不变
        // TODO 原则是 用户只有在审核通过的时候才能影响 user_profile 字段
        // $profile = UserProfile::query()->where('user_id', $userProfileFormat->getUserId())->first();
        // if ($profile) {
        //    $profile->check_status = CheckStatusEnum::CHECK_ING;
        //    $profile->save();
        // }
        return $res;
    }

    /**
     * 后台审核.
     *
     * @throws ApiException
     * @return bool
     */
    public function checkUserProfile(int $userId, int $check_status, string $check_message)
    {
        /** @var CheckUserProfile $checkData */
        $checkData = CheckUserProfile::query()->where('user_id', $userId)->first();
        if (empty($checkData)) {
            throw new ApiException('此信息不存在', 1);
        }

        if ($check_status != CheckStatusEnum::CHECK_ADOPT) {
            $checkData->check_status = $check_status;
            $checkData->check_message = $check_message;
            $checkData->save();
            adminLog(new UserProfile(), AdminActionTypeEnum::UPDATE, '审核用户未通过');
            event(new CheckSuccess(new CheckSuccessFormat([
                'user_id' => $userId,
                'check_type' => AdminCheckEnum::CHECK_FLOW_USER,
                'check_status' => $check_status,
            ])));
            return true;
        }

        $isCompany = IsCompanyEnum::NO;
        if (in_array($checkData->area, [AreaEnum::COMPANY_ONE, AreaEnum::COMPANY_TWO, AreaEnum::COMPANY_THREE])) {
            $isCompany = IsCompanyEnum::YES;
            Log::info(sprintf('用户 【%s】是企业角色', $userId), [
                'data' => $checkData->toArray(),
            ]);
        }

        // 审核通过就把审核表的删除 写到 profile
        try {
            DB::beginTransaction();
            // 如果有之前的数据（可能是后台导入的） 使用传入的数据覆盖
            /** @var null|UserProfile $old */
            $old = UserProfile::query()->where('id_card_number', $checkData->id_card_number)->first();
            /** @var User $userInfo */
            $userInfo = User::query()->find($userId);
            $userInfo->is_company = $isCompany;
            $userInfo->check_status = $check_status;
            $userInfo->save();
            if (! empty($old)) {
                $checkArray = $checkData->toArray();
                unset($checkArray['id'], $checkArray['check_message']);
                $old->update($checkArray);
                $old->check_status = CheckStatusEnum::CHECK_ADOPT;
                $old->is_company = $isCompany;
                $old->save();
            } else {
                // 如果是用户第一次录入或者修改了自己的身份证号码的情况
                // 等于是先用身份证找一次 如果找到就更新信息
                // 找不到就尝试用 user_id 找一次 找到就更新信息 找不到就创建
                /** @var null|UserProfile $profile */
                $profile = UserProfile::query()->where('user_id', $checkData->user_id)->first();
                $checkArray = $checkData->toArray();
                unset($checkArray['id'], $checkArray['check_message']);

                if (! $profile) {
                    $profile = UserProfile::query()->create($checkArray);
                } else {
                    // 如果有 可能是之前设置过的
                    $profile->update($checkArray);
                }
                $profile->check_status = CheckStatusEnum::CHECK_ADOPT;
                $profile->is_company = $isCompany;
                $profile->save();
            }
            $checkData->delete();
            DB::commit();
        } catch (\Exception $exception) {
            Log::error('审核失败', ['data' => $exception->getMessage()]);
            DB::rollBack();
            throw new ApiException('审核失败, 系统异常', 1);
        }
        adminLog(new UserProfile(), AdminActionTypeEnum::UPDATE, '审核用户通过');
        event(new CheckSuccess(new CheckSuccessFormat([
            'user_id' => $userId,
            'check_type' => AdminCheckEnum::CHECK_FLOW_USER,
            'check_status' => $check_status,
        ])));
        return true;
    }

    public function getUserInfo(int $userId)
    {
        $user = User::query()->select('id', 'name', 'phone', 'sex', 'avatar', 'identity')->find($userId)->toArray();
        if (substr($user['avatar'], 0, 4) != 'http') {
            $user['avatar'] = config('app.url') . $user['avatar'];
        }
        $user['message_num'] = $this->getUserMessageCount($userId);
        return $user;
    }

    public function getUserMessageCount(int $userId)
    {
        return Message::query()->where('user_id', $userId)->count();
    }

    public function getUserProfile(int $userId)
    {
        // 审核中
        $checkData = CheckUserProfile::query()->with('industryEnum')->where('user_id', $userId)->first();
        if ($checkData) {
            return $checkData->toArray();
        }
        // 审核通过 就从 profile 中获取了
        $profile = UserProfile::query()->with('industryEnum')->where('user_id', $userId)->where('check_status', CheckStatusEnum::CHECK_ADOPT)
            ->first();
        if ($profile) {
            return $profile->toArray();
        }
        // 还什么都没有
        return new \stdClass();
    }

    public function checkLogin(int $userId, string $token)
    {
        if (! User::query()->find($userId) && ! AdminUser::query()->find($userId)) {
            return false;
        }
        $userLoginTokenKey = sprintf(self::USER_TOKEN_KEY, $userId);
        $redisToken = Redis::connection()->get($userLoginTokenKey);
        $adminLoginTokenKey = sprintf(self::ADMIN_USER_TOKEN_KEY, $userId);
        $adminLoginToken = Redis::connection()->get($adminLoginTokenKey);
        if ($redisToken != $token && $adminLoginToken != $token) {
            return false;
        }
        if ($redisToken == $token) {
            Redis::expire($userLoginTokenKey, 3600);
        }
        if ($adminLoginToken == $token) {
            Redis::expire($adminLoginTokenKey, 3600);
        }
        return true;
    }

    public function resetPassword(string $phone, int $password_question_type, string $password_question_answer, string $newPassword)
    {
        /** @var null|User $user */
        $user = User::query()->where('phone', $phone)->first();
        if (is_null($user)) {
            throw new ApiException('用户不存在', 1);
        }
        if ($user->password_question_answer == $password_question_answer && $user->password_question_type == $password_question_type) {
            $user->password = md5($newPassword);
            $user->save();
            return true;
        }
        throw new ApiException('密保问题异常', 1);
    }

    public function saveLoginToken(int $userId): array
    {
        $token = $this->generToken();
        $this->setToken($userId, $token);
        return [
            'user_id' => $userId,
            'token' => $token,
        ];
    }

    // 管理员获取用户列表(流动人口)
    public function getAdminUserList(int $page, int $pageSize, array $search)
    {
        // 如果 check 表存在 用 check 的
        $builder = UserProfile::query()
            ->with([
                'user:id,name,phone,sex',
                'check:user_id,check_status,id_card_number',
                'industryEnum',
                'user.check:user_id,check_status',
                'assessLog' => function ($builder) {
                    /* @var \Illuminate\Database\Eloquent\Builder $builder */
                    $builder->where('assess_time', '>=', strtotime(date('Y') . '-01-01'));
                    $builder->where('assess_time', '<=', strtotime(date('Y') . '-12-31'));
                    $builder->with([
                        'userProfile' => function ($query) {
                            $query->where('check_status', CheckStatusEnum::CHECK_ADOPT);
                        },
                    ]);
                },
            ])
            ->where('identity', UserIdentityEnum::FLOW_USER);
        if (isset($search['name'])) {
            if (! isset($search['check_status'])) {
                $builder->where('name', 'like', '%' . $search['name'] . '%');
                $builder->orWhereHas('user.check', function ($query) use ($search) {
                    $query->where('name', 'like', '%' . $search['name'] . '%');
                });
            } else {
                if ($search['check_status'] == CheckStatusEnum::CHECK_ING || $search['check_status'] == CheckStatusEnum::CHECK_REFUSE) {
                    $builder->whereHas('user.check', function ($query) use ($search) {
                        $query->where('name', 'like', '%' . $search['name'] . '%');
                    });
                } elseif ($search['check_status'] == CheckStatusEnum::DRAFT || $search['check_status'] == CheckStatusEnum::CHECK_ADOPT) {
                    $builder->where('name', 'like', '%' . $search['name'] . '%');
                } elseif ($search['check_status'] == CheckStatusEnum::NOT_FILL) {
                    $builder->where('name', 'like', '%' . $search['name'] . '%');
                }
            }
        }
        if (isset($search['check_status'])) {
            if ($search['check_status'] == CheckStatusEnum::CHECK_ADOPT || $search['check_status'] == CheckStatusEnum::DRAFT) {
                $builder->where('check_status', $search['check_status']);
                $builder->whereDoesntHave('check');
                $builder->whereDoesntHave('user.check');
            } elseif ($search['check_status'] == CheckStatusEnum::NOT_FILL) {
                $builder->where('check_status', $search['check_status']);
                $builder->whereDoesntHave('check');
                $builder->whereDoesntHave('user.check');
            } else {
                $builder->whereHas('user.check', function ($rbuilder) use ($search) {
                    $rbuilder->where('check_status', $search['check_status']);
                });
            }
        }
        if (isset($search['now_address'])) {
            $builder->where('now_address', 'like', '%' . $search['now_address'] . '%');
        }

        if (isset($search['successive_not_in_village_days']) && is_numeric($search['successive_not_in_village_days'])) {
            $builder->where('successive_not_in_village_days', '>=', $search['successive_not_in_village_days']);
        }
        if (isset($search['area'])) {
            if (isset($search['check_status']) && $search['check_status'] == CheckStatusEnum::CHECK_ADOPT) {
                $builder->where('area', $search['area']);
            }
            if (isset($search['check_status']) && $search['check_status'] != CheckStatusEnum::CHECK_ADOPT) {
                $builder->whereHas('user.check', function ($rbuilder) use ($search) {
                    $rbuilder->where('area', $search['area']);
                });
            }
            if (empty($search['check_status'])) {
                $builder->where('area', $search['area'])->orWhereHas('user.check', function ($rbuilder) use ($search) {
                    $rbuilder->where('area', $search['area']);
                });
            }
        }

        $count = $builder->count();
        $list = $builder->select(
            'name',
            'sex',
            'phone',
            'identity',
            'now_address',
            'check_status',
            'id',
            'user_id',
            'industry',
            'id_card_number',
            'successive_not_in_village_days',
            'area'
        )
            ->skip(($page - 1) * $pageSize)
            ->take($pageSize)
            ->get()->toArray();
        return [
            'list' => $list,
            'count' => $count,
            'page' => $page,
            'page_size' => $pageSize,
        ];
    }

    public function getExportData(array $search)
    {
        $builder = UserProfile::query()
            ->with(['user:id,name,phone,sex', 'check:user_id,check_status,id_card_number', 'industryEnum',
                'assessLog' => function ($builder) {
                    $builder->where('assess_time', '>=', strtotime(date('Y') . '-01-01'));
                    $builder->where('assess_time', '<=', strtotime(date('Y') . '-12-31'));
                },
            ])
            ->whereIn('identity', $search['identity']);
        if (isset($search['name'])) {
            if ($search['check_status'] == CheckStatusEnum::CHECK_ING || $search['check_status'] == CheckStatusEnum::CHECK_REFUSE) {
                $builder->whereHas('user.check', function ($query) use ($search) {
                    $query->where('name', 'like', '%' . $search['name'] . '%');
                });
            } elseif ($search['check_status'] == CheckStatusEnum::DRAFT || $search['check_status'] == CheckStatusEnum::CHECK_ADOPT) {
                $builder->where('name', 'like', '%' . $search['name'] . '%');
            } elseif ($search['check_status'] == CheckStatusEnum::NOT_FILL) {
                $builder->where('name', 'like', '%' . $search['name'] . '%');
            }
        }
        if (isset($search['check_status'])) {
            if ($search['check_status'] == CheckStatusEnum::CHECK_ADOPT || $search['check_status'] == CheckStatusEnum::DRAFT) {
                $builder->where('check_status', $search['check_status']);
                $builder->whereDoesntHave('check');
                $builder->whereDoesntHave('user.check');
            } elseif ($search['check_status'] == CheckStatusEnum::NOT_FILL) {
                $builder->where('check_status', $search['check_status']);
                $builder->whereDoesntHave('check');
                $builder->whereDoesntHave('user.check');
            } else {
                $builder->whereHas('user.check', function ($rbuilder) use ($search) {
                    $rbuilder->where('check_status', $search['check_status']);
                });
            }
        }
        if (isset($search['now_address'])) {
            $builder->where('now_address', 'like', '%' . $search['now_address'] . '%');
        }
        if (isset($search['ids'])) {
            $builder->whereIn('id', $search['ids']);
        }
        if (isset($search['identity'])) {
            $builder->whereIn('identity', $search['identity']);
        }
        if (isset($search['successive_not_in_village_days']) && is_numeric($search['successive_not_in_village_days'])) {
            $builder->where('successive_not_in_village_days', '>=', $search['successive_not_in_village_days']);
        }
        if (isset($search['area'])) {
            if (isset($search['check_status']) && $search['check_status'] == CheckStatusEnum::CHECK_ADOPT) {
                $builder->where('area', $search['area']);
            }
            if (isset($search['check_status']) && $search['check_status'] != CheckStatusEnum::CHECK_ADOPT) {
                $builder->whereHas('user.check', function ($rbuilder) use ($search) {
                    $rbuilder->where('area', $search['area']);
                });
            }
            if (empty($search['check_status'])) {
                $builder->where('area', $search['area'])->orWhereHas('user.check', function ($rbuilder) use ($search) {
                    $rbuilder->where('area', $search['area']);
                });
            }
        }

        adminLog(new UserProfile(), AdminActionTypeEnum::SELECT, '导出用户数据');

        $list = $builder->get();
        //导出数据防止身份证后三位为0
        foreach ($list as $k => $v) {
            $list[$k]->id_card_number = (string)$v->id_card_number;
        }
        return $list;
    }

    public function getAdminNotFlowList(int $page, int $pageSize, array $search)
    {
        // 如果 check 表存在 用 check 的
        $builder = UserProfile::query()
            ->with([
                'user:id,name,phone,sex',
                'check:user_id,check_status,id_card_number,name',
                'industryEnum',
                'user.check:user_id,check_status,name',
                'assessLog' => function ($builder) {
                    /* @var \Illuminate\Database\Eloquent\Builder $builder */
                    $builder->where('assess_time', '>=', strtotime(date('Y') . '-01-01'));
                    $builder->where('assess_time', '<=', strtotime(date('Y') . '-12-31'));
                    $builder->with([
                        'userProfile' => function ($query) {
                            $query->where('check_status', CheckStatusEnum::CHECK_ADOPT);
                        },
                    ]);
                },
            ])
            ->whereIn('identity', [UserIdentityEnum::HOUSER, UserIdentityEnum::NORMAL]);
        if (isset($search['name'])) {
            if (! isset($search['check_status'])) {
                $builder->where('name', 'like', '%' . $search['name'] . '%');
                $builder->orWhereHas('user.check', function ($query) use ($search) {
                    $query->where('name', 'like', '%' . $search['name'] . '%');
                });
            } else {
                if ($search['check_status'] == CheckStatusEnum::CHECK_ING || $search['check_status'] == CheckStatusEnum::CHECK_REFUSE) {
                    $builder->whereHas('user.check', function ($query) use ($search) {
                        $query->where('name', 'like', '%' . $search['name'] . '%');
                    });
                } elseif ($search['check_status'] == CheckStatusEnum::DRAFT || $search['check_status'] == CheckStatusEnum::CHECK_ADOPT) {
                    $builder->where('name', 'like', '%' . $search['name'] . '%');
                } elseif ($search['check_status'] == CheckStatusEnum::NOT_FILL) {
                    $builder->where('name', 'like', '%' . $search['name'] . '%');
                }
            }
        }
        if (isset($search['check_status'])) {
            if ($search['check_status'] == CheckStatusEnum::CHECK_ADOPT || $search['check_status'] == CheckStatusEnum::DRAFT) {
                $builder->where('check_status', $search['check_status']);
                $builder->whereDoesntHave('check');
                $builder->whereDoesntHave('user.check');
            } elseif ($search['check_status'] == CheckStatusEnum::NOT_FILL) {
                $builder->where('check_status', $search['check_status']);
                $builder->whereDoesntHave('check');
                $builder->whereDoesntHave('user.check');
            } else {
                $builder->whereHas('user.check', function ($rbuilder) use ($search) {
                    $rbuilder->where('check_status', $search['check_status']);
                });
            }
        }
        if (isset($search['now_address'])) {
            $builder->where('now_address', 'like', '%' . $search['now_address'] . '%');
        }
        if (isset($search['successive_not_in_village_days']) && is_numeric($search['successive_not_in_village_days'])) {
            $builder->where('successive_not_in_village_days', '>=', $search['successive_not_in_village_days']);
        }
        if (isset($search['area'])) {
            if (isset($search['check_status']) && $search['check_status'] == CheckStatusEnum::CHECK_ADOPT) {
                $builder->where('area', $search['area']);
            }
            if (isset($search['check_status']) && $search['check_status'] != CheckStatusEnum::CHECK_ADOPT) {
                $builder->whereHas('user.check', function ($rbuilder) use ($search) {
                    $rbuilder->where('area', $search['area']);
                });
            }
            if (empty($search['check_status'])) {
                $builder->where('area', $search['area'])->orWhereHas('user.check', function ($rbuilder) use ($search) {
                    $rbuilder->where('area', $search['area']);
                });
            }
        }
        $count = $builder->count();
        $list = $builder->select(
            'name',
            'sex',
            'phone',
            'identity',
            'now_address',
            'check_status',
            'id',
            'user_id',
            'industry',
            'id_card_number',
            'successive_not_in_village_days',
            'area'
        )
            ->skip(($page - 1) * $pageSize)
            ->take($pageSize)
            ->get()->toArray();
        return [
            'list' => $list,
            'count' => $count,
            'page' => $page,
            'page_size' => $pageSize,
        ];
    }

    public function adminUpdateUserProfile(UserProfileFormat $userProfileFormat)
    {
        $id = $userProfileFormat->getId();
        if (empty($id)) {
            throw new ApiException('缺少参数', 1);
        }
        $userProfileFormat->setId(null);
        $userProfileFormat->setUserId(null);
        $data = $userProfileFormat->toArrayNotNull();
        $profile = UserProfile::query()->find($id);
        $profile->update($data);
        adminLog($profile, AdminActionTypeEnum::UPDATE, '修改用户信息');
        return true;
    }

    public function createUserFromAdmin(UserProfileFormat $userProfileFormat)
    {
        $lockKey = 'create_user_from_admin';
        if (Redis::connection()->get($lockKey)) {
            throw new ApiException('创建请求过于频繁', 1);
        }
        if (empty($userProfileFormat->getIdCardNumber())) {
            throw new ApiException('缺少身份证信息', 1);
        }
        $userProfile = UserProfile::query()->where('id_card_number', $userProfileFormat->getIdCardNumber())->exists();
        // 如果已经存在了 就跳过
        if ($userProfile) {
            Log::error(sprintf('身份证 【%s】 已经存在', $userProfileFormat->getIdCardNumber()));
            return true;
        }
        $userProfileFormat->setUserId(null);
        $userProfileFormat->setCheckStatus(CheckStatusEnum::CHECK_ADOPT);
        Redis::connection()->set($lockKey, '1');
        UserProfile::query()->create($userProfileFormat->toArrayNotNull());
        Redis::connection()->del($lockKey);
        adminLog(new UserProfile(), AdminActionTypeEnum::CREATE, '创建用户信息');
        return true;
    }

    public function findUserHouser(int $userId)
    {
        /** @var null|UserProfile $userProfile */
        $userProfile = UserProfile::query()->where('user_id', $userId)->first();
        if (empty($userProfile)) {
            throw new ApiException('没有录入信息, 不能匹配到房东', 1);
        }
        /** @var HouseUserBind $houseUserBind */
        $houseUserBind = HouseUserBind::query()->where('id_card_number', $userProfile->id_card_number)->first();
        if (empty($houseUserBind)) {
            throw new ApiException('没有查询到绑定的房东信息');
        }
        /** @var House $house */
        $house = House::query()->where('id', $houseUserBind->house_id)->select('houser_user_id')->first();
        /** @var User $houser */
        $houser = User::query()->find($house->houser_user_id);
        if ($houser->identity != UserIdentityEnum::HOUSER) {
            throw new ApiException('没有查询到绑定的房东信息');
        }
        return $houser;
    }

    public function getMyRentedHouse(int $userId)
    {
        /** @var null|UserProfile $userProfile */
        $userProfile = UserProfile::query()->select('id_card_number')->where('user_id', $userId)->first();
        if (empty($userProfile)) {
            throw new ApiException('没有录入个人信息或者没有通过审核, 无法查询房间', 1);
        }

        /** @var null|HouseUserBind $houseUserBind */
        $houseUserBind = HouseUserBind::query()->where('id_card_number', $userProfile->id_card_number)->first();
        if (empty($houseUserBind)) {
            throw new ApiException('没有查询到绑定的房屋信息', 1);
        }
        $houseId = $houseUserBind->house_id;
        $house = House::query()->with([
            'userScore' => function ($builder) use ($userId) {
                $builder->where('user_id', $userId);
            },
        ])->where('id', $houseId)->first()->toArray();
        $houser = UserProfile::query()
            ->select('name', 'address')
            ->where('user_id', $house['houser_user_id'])
            ->first();
        $house['houser_name'] = $houser->name;
        $house['houser_address'] = $houser->address;
        return $house;
    }

    public function setPassCardInfo($flowRegisterCode, string $passCardPhoto, int $userId)
    {
        /** @var UserProfile $userProfile */
        $userProfile = UserProfile::query()->where('user_id', $userId)->first();
        if ($userProfile->check_status != CheckStatusEnum::CHECK_ADOPT || empty($userProfile)) {
            throw new ApiException('个人信息未审核, 不能录入出入证信息', 1);
        }
        PassCard::query()->updateOrCreate(
            ['user_id' => $userId],
            ['flow_register_code' => $flowRegisterCode, 'pass_card_photo' => $passCardPhoto, 'user_id' => $userId, 'check_status' => CheckStatusEnum::CHECK_ING],
        );
        return true;
    }

    public function getPassCardInfo(int $userId)
    {
        /** @var null|UserProfile $profile */
        $profile = UserProfile::query()->where('user_id', $userId)
            ->select(
                'now_address',
                'id_card_number',
                'phone',
                'name',
                'company_name',
                'is_company',
                'identity',
                'urgent_people',
                'urgent_people_phone',
            )->first();

        if (empty($profile)) {
            throw new ApiException('未录入个人信息', 1);
        }
        $passCardInfo = $profile->toArray();
        if ($passCardInfo['is_company'] == IsCompanyEnum::NO) {
            unset($passCardInfo['company_name']);
        }
        $passCardInfo['flow_register_code'] = '';
        $passCardInfo['pass_card_photo'] = '';
        $passCardInfo['check_status'] = '';
        $passCardInfo['check_message'] = '';
        $passCardInfo['houser_phone'] = '';
        /** @var null|PassCard $passCard */
        $passCard = PassCard::query()->where('user_id', $userId)->first();
        if (empty($passCard)) {
            return $passCardInfo;
        }

        if (! empty($passCard)) {
            $passCardInfo['flow_register_code'] = $passCard->flow_register_code;
            $passCardInfo['pass_card_photo'] = $passCard->pass_card_photo;
            $passCardInfo['check_status'] = $passCard->check_status;
            $passCardInfo['check_message'] = $passCard->check_message;
        }
        try {
            $houser = $this->findUserHouser($userId);
            $passCardInfo['houser_phone'] = $houser['phone'];
        } catch (ApiException $apiException) {
            $passCardInfo['houser_phone'] = $apiException->getMessage();
        }
        return $passCardInfo;
    }

    public function checkPassCard(int $id, int $checkStatus, string $checkMessage)
    {
        /** @var null|PassCard $passCard */
        $passCard = PassCard::query()->find($id);
        if (empty($passCard)) {
            throw new ApiException('信息不存在', 1);
        }
        $passCard->check_status = $checkStatus;
        $passCard->check_message = $checkMessage;
        $passCard->save();
        adminLog($passCard, AdminActionTypeEnum::UPDATE, '审核出入证-' . CheckStatusEnum::desc($checkStatus));
        event(new CheckSuccess(new CheckSuccessFormat([
            'check_type' => AdminCheckEnum::CHECK_PASS_CARD,
            'check_status' => $checkStatus,
            'user_id' => $passCard->user_id,
        ])));
        return true;
    }

    public function houserList(int $page, int $pageSize, array $search)
    {
        $builder = UserProfile::query()
            ->with([
                'house',
                'score_result',
            ])
            ->select('id', 'name', 'user_id', 'houser_score', 'need_check_house_number', 'check_adopt_house_number')
            ->whereIn('identity', [UserIdentityEnum::HOUSER, UserIdentityEnum::NORMAL]);

        $builder->whereHas('house');
        if (isset($search['name'])) {
            $builder->where('name', 'like', '%' . $search['name'] . '%');
        }
        /*
         * need_check_house 1: 房屋全部通过
         * need_check_house 3: 还有房屋没通过
         */
        if (isset($search['need_check_house'])) {
            if ($search['need_check_house'] == 3) {
                $builder->where('need_check_house_number', '>', 0);
            }
            if ($search['need_check_house'] == 1) {
                $builder->where('need_check_house_number', 0);
            }
        }
        if (isset($search['order_column'])) {
            $search['order_type'] = $search['order_type'] ?? 'desc';
            $builder->orderBy($search['order_column'], $search['order_type']);
        }
        $count = $builder->count();
        $list = $builder->skip(($page - 1) * $pageSize)->take($pageSize)->get()->map(function ($row) {
            return [
                'id' => $row->user_id,
                'user_profile_id' => $row->id,
                'name' => $row->name,
                'house' => $row->house,
                'houser_score' => $row->houser_score,
                'score_config' => $row->score_result,
                // 需要审核的房屋数量
                'need_check_house_number' => $row->need_check_house_number,
                'check_adopt_house_number' => $row->check_adopt_house_number,
                'can_intervene_score' => $row->check_adopt_house_number > 0,
            ];
        });
        if (! empty($search)) {
            //adminLog(new User(), AdminActionTypeEnum::SELECT, '查询房东信息');
        }
        return [
            'page' => $page,
            'page_size' => $pageSize,
            'count' => $count,
            'list' => $list,
        ];
    }

    public function houserDetail(int $houserId)
    {
        adminLog(new UserProfile(), AdminActionTypeEnum::SELECT, '查询房东详情');
        return UserProfile::query()->with(['houses', 'houses.tenants'])->where('user_id', $houserId)->first();
    }

    public function deleteUserProfile(int $id)
    {
        DB::beginTransaction();
        try {
            /** @var UserProfile $userProfile */
            $userProfile = UserProfile::query()->find($id);
            $userProfile->check_status = CheckStatusEnum::NOT_FILL;
            $userId = $userProfile->user_id;
            CheckUserProfile::query()->where('user_id', $userId)->delete();
            User::query()->where('id', $userId)->delete();
            Message::query()->where('user_id', $userId)->delete();
            Proposal::query()->where('user_id', $userId)->delete();
            UserAssessLog::query()->where('user_profile_id', $id)->delete();
            PassCard::query()->where('user_id', $userId)->delete();
            UserMessageLog::query()->where('user_id', $userId)->delete();
            RentHouse::query()->where('user_id', $userId)->delete();
            QingfengProposal::query()->where('user_id', $userId)->delete();
            SearchNotice::query()->where('user_id', $userId)->delete();
            Recruitment::query()->where('user_id', $userId)->delete();
            HouserScore::query()->where('user_id', $userId)->delete();
            UserProfile::query()->where('id', $id)->delete();
            $houses = House::query()->where('houser_user_id', $userId)
                ->select('houser_user_id', 'id')
                ->get();
            if ($houses->count() > 0) {
                HouserScore::query()->whereIn('house_id', $houses->pluck('id'))->delete();
                HouserScoreResult::query()->where('houser_id', $userId)->delete();
            }
            UserPosition::query()->where('user_id', $userId)->delete();
            $userProfile->user_id = 0;
            $userProfile->save();
            DB::commit();
            adminLog($userProfile, AdminActionTypeEnum::DELETE, '删除用户');
            Log::info('删除用户 清除所有用户信息', [
                'user_data' => $userProfile->toArray(),
            ]);
            return true;
        } catch (\Exception $e) {
            DB::rollBack();
            throw new ApiException($e->getMessage(), 1);
        }
    }

    public function updatePassword(string $oldPassword, string $newPassword, int $id)
    {
        /** @var User $user */
        $user = User::query()->find($id);
        if (md5($oldPassword) != $user->password) {
            throw new ApiException('密码错误', 1);
        }
        $user->password = md5($newPassword);
        $user->save();
        return true;
    }

    public function setUserPosition(int $userId, string $latitude, string $longitude)
    {
        Log::debug('记录用户上报的经纬度', [
            'latitude' => $latitude,
            'longitude' => $longitude,
        ]);
        $key = sprintf(self::USER_POSITION_KEY, $userId);
        if (Redis::connection()->get($key)) {
            return true;
        }
        $format = new UserPositionFormat();
        $format->setUserId($userId);
        $format->setLatitude($latitude);
        $format->setLongitude($longitude);
        $format->setReportTime(time());
        $format->setIsLocal(checkInLocation($latitude, $longitude) ? UserIsLocalEnum::IN_LOCAL : UserIsLocalEnum::NOT_LOCAL);
        UserPosition::query()->create($format->toArrayNotNull());
        Redis::connection()->set($key, 'lock');
        Redis::expire($key, 3600);
        return true;
    }

    protected function changeUserProfile(UserProfileFormat $userProfileFormat): bool
    {
        /** @var UserProfile $userProfile */
        $userProfile = UserProfile::query()->find($userProfileFormat->getId());
        if (empty($userProfile)) {
            throw new ApiException('此信息不存在', 1);
        }
        $userProfile->update($userProfileFormat->toArrayNotNull());
        return true;
    }

    protected function changeCheckProfile(UserProfileFormat $userProfileFormat)
    {
        $checkProfile = CheckUserProfile::query()->find($userProfileFormat->getId());
        $checkProfileFromUserId = CheckUserProfile::query()->where('user_id', $userProfileFormat->getUserId())->first();
        if (! $checkProfile && ! $checkProfileFromUserId) {
            // 这就是之前审核通过后删了
            $userProfileFormat->setId(null);
            return CheckUserProfile::query()->create($userProfileFormat->toArrayNotNull());
        }
        if ($checkProfile) {
            return $checkProfile->update($userProfileFormat->toArrayNotNull());
        }
        return $checkProfileFromUserId->update($userProfileFormat->toArrayNotNull());
    }

    private function getUserPositionLog(int $userId)
    {
        $user = User::query()->find($userId);
        $res = [];
        if (! empty($user)) {
            $startMonth = date('Y-m', strtotime($user->created_at));
            $endMonth = date('Y-m', time());
            $monthList = $this->genMonth($startMonth, $endMonth);
            $userPosition = UserPosition::query()
                ->where('user_id', $userId)
                ->where('is_local', UserIsLocalEnum::IN_LOCAL)
                ->get()
                ->map(function (UserPosition $position) {
                    return [
                        'report_month' => date('Y-m', $position->report_time),
                        'is_local' => $position->is_local,
                    ];
                })
                ->toArray();

            $userPositionMap = array_column($userPosition, null, 'report_month');
            foreach ($monthList as $month) {
                array_push($res, [
                    'month' => $month,
                    'is_local' => isset($userPositionMap[$month]) ? UserIsLocalEnum::IN_LOCAL : UserIsLocalEnum::NOT_LOCAL,
                ]);
            }
        }

        return $res;
    }

    private function genMonth(string $startMonth, string $endMonth): array
    {
        if ($startMonth == $endMonth) {
            return [$startMonth];
        }
        $res = [];
        array_push($res, $startMonth);
        while (true) {
            $startMonth = date('Y-m', strtotime('+1 month', strtotime($startMonth)));
            array_push($res, $startMonth);
            if ($startMonth == $endMonth) {
                break;
            }
        }
        return $res;
    }

    private function setAdminLoginToken(int $adminUserId, string $token)
    {
        $userLoginTokenKey = sprintf(self::ADMIN_USER_TOKEN_KEY, $adminUserId);
        Redis::connection()->set($userLoginTokenKey, $token);
        Redis::expire($userLoginTokenKey, 7200);
    }

    private function groupUserAccess(array $userAccessLogs)
    {
        if (empty($userAccessLogs)) {
            return [];
        }
        $res = [];
        $ruleScoreMap = array_column($userAccessLogs, null, 'assess_template_rule_id');
        foreach ($userAccessLogs as $userAccessLog) {
            $assessTemplate = $userAccessLog['assess_template'];
            if (empty($assessTemplate)) {
                Log::error(sprintf('模版 id 【%s】 丢失', $userAccessLog['assess_template_id']));
                return [];
            }
            if (checkArrayExistsInArray($res, $assessTemplate, 'id')) {
                continue;
            }
            $rules = $assessTemplate['rules'];
            foreach ($rules as &$rule) {
                $rule['score'] = $ruleScoreMap[$rule['id']]['score'] ?? 0;
            }
            $assessTemplate['rules'] = $rules;
            array_push($res, $assessTemplate);
        }
        return $res;
    }

    /**
     * @return \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Model|User
     */
    private function createUserFromWxData(WxUserInfoDecryptDataFormat $wxUserInfoDecryptDataFormat)
    {
        $userFormat = new UserFormat();
        $userFormat->setWxNickName($wxUserInfoDecryptDataFormat->getNickName());
        $userFormat->setName($wxUserInfoDecryptDataFormat->getNickName());
        $userFormat->setWxOpenid($wxUserInfoDecryptDataFormat->getOpenid());
        $userFormat->setSex($wxUserInfoDecryptDataFormat->getGender());
        $userFormat->setAvatar($wxUserInfoDecryptDataFormat->getAvatarUrl());
        $userFormat->setName($wxUserInfoDecryptDataFormat->getNickName());
        $userFormat->setCheckStatus(CheckStatusEnum::NOT_FILL);
        /* @var User $user */
        return User::query()->firstOrCreate($userFormat->toArrayNotNull());
    }

    private function generToken(): string
    {
        return Str::random(36);
    }

    private function setToken(int $userId, string $token)
    {
        $userLoginTokenKey = sprintf(self::USER_TOKEN_KEY, $userId);
        Redis::connection()->set($userLoginTokenKey, $token);
        Redis::expire($userLoginTokenKey, 3600);
    }

    private function genVcode(): int
    {
        return rand(0000, 9999);
    }

    private function setVcode(int $userId, int $vcode)
    {
        $vcodeKey = sprintf(self::USER_VCODE_KEY, $userId);
        Redis::connection()->set($vcodeKey, $vcode);
        Redis::expire($vcodeKey, 100);
    }

    private function findVcode(int $userId): int
    {
        $vcode = Redis::connection()->client()->get(sprintf(self::USER_VCODE_KEY, $userId));
        if (! $vcode) {
            Log::error(sprintf('user %s vcode not found', $userId));
            throw new ApiException('密码错误', 1);
        }
        return intval($vcode);
    }

    private function findAdminVcode(int $adminUserId): int
    {
        $vcode = Redis::connection()->client()->get(sprintf(self::ADMIN_USER_VCODE_KEY, $adminUserId));
        if (! $vcode) {
            Log::error(sprintf('user %s vcode not found', $adminUserId));
            throw new ApiException('密码错误', 1);
        }
        return intval($vcode);
    }

    private function setAdmintVcode(int $adminUserId, int $vcode)
    {
        $vcodeKey = sprintf(self::ADMIN_USER_VCODE_KEY, $adminUserId);
        Redis::connection()->set($vcodeKey, $vcode);
        Redis::expire($vcodeKey, 100);
    }
}
